// SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
// SPDX-License-Identifier: MIT

package utils

import (
	"strings"
	"testing"
)

func TestSecurityUtils_SanitizePath(t *testing.T) {
	su := NewSecurityUtils()

	// 测试基本的路径脱敏功能
	tests := []struct {
		name             string
		input            string
		shouldContain    []string // 结果应该包含的字符串
		shouldNotContain []string // 结果不应该包含的字符串
	}{
		{
			name:             "Unix home directory",
			input:            "/home/user/project/config.yaml",
			shouldContain:    []string{"config.yaml"},
			shouldNotContain: []string{"/home/user/"},
		},
		{
			name:             "Root user directory",
			input:            "/root/secret-project/config.ini",
			shouldContain:    []string{"config.ini"},
			shouldNotContain: []string{"/root/"},
		},
		{
			name:             "Etc configuration",
			input:            "/etc/uwsgi/app.ini",
			shouldContain:    []string{"app.ini"},
			shouldNotContain: []string{"/etc/uwsgi/"},
		},
		{
			name:             "Var log directory",
			input:            "/var/log/uwsgi/app.log",
			shouldContain:    []string{"app.log"},
			shouldNotContain: []string{"/var/log/uwsgi/"},
		},
		{
			name:             "Relative path unchanged",
			input:            "./config/test.yaml",
			shouldContain:    []string{"./config/test.yaml"},
			shouldNotContain: []string{},
		},
		{
			name:             "Simple filename",
			input:            "config.yaml",
			shouldContain:    []string{"config.yaml"},
			shouldNotContain: []string{},
		},
		{
			name:             "Empty string",
			input:            "",
			shouldContain:    []string{},
			shouldNotContain: []string{},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := su.SanitizePath(tt.input)

			// 检查应该包含的字符串
			for _, contain := range tt.shouldContain {
				if !strings.Contains(result, contain) {
					t.Errorf("SanitizePath() result %q should contain %q", result, contain)
				}
			}

			// 检查不应该包含的字符串
			for _, notContain := range tt.shouldNotContain {
				if strings.Contains(result, notContain) {
					t.Errorf("SanitizePath() result %q should not contain %q", result, notContain)
				}
			}
		})
	}
}

func TestSecurityUtils_SanitizeErrorMessage(t *testing.T) {
	su := NewSecurityUtils()

	tests := []struct {
		name        string
		input       string
		contains    []string // 结果应该包含的字符串
		notContains []string // 结果不应该包含的字符串
	}{
		{
			name:        "Password in error message",
			input:       "Failed to authenticate: password=mysecretpassword",
			contains:    []string{"Failed to authenticate", "[REDACTED]"},
			notContains: []string{"mysecretpassword"},
		},
		{
			name:        "API key in error message",
			input:       "Invalid API key: api_key=sk-1234567890abcdef",
			contains:    []string{"Invalid API key", "[REDACTED]"},
			notContains: []string{"sk-1234567890abcdef"},
		},
		{
			name:        "Database connection string",
			input:       "Failed to connect: mongodb://user:pass@localhost:27017/db",
			contains:    []string{"Failed to connect", "[REDACTED]"},
			notContains: []string{"user:pass"},
		},
		{
			name:        "Bearer token",
			input:       "Authentication failed: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",
			contains:    []string{"Authentication failed", "[REDACTED]"},
			notContains: []string{"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"},
		},
		{
			name:        "Basic auth",
			input:       "HTTP 401: Basic dXNlcjpwYXNz",
			contains:    []string{"HTTP 401", "[REDACTED]"},
			notContains: []string{"dXNlcjpwYXNz"},
		},
		{
			name:        "Path and credential combined",
			input:       "Failed to read /home/user/config.yaml: password=secret123",
			contains:    []string{"Failed to read", "config.yaml", "[REDACTED]"},
			notContains: []string{"/home/user/", "secret123"},
		},
		{
			name:        "Normal error message unchanged",
			input:       "File not found: config.yaml",
			contains:    []string{"File not found: config.yaml"},
			notContains: []string{},
		},
		{
			name:        "Empty message",
			input:       "",
			contains:    []string{},
			notContains: []string{},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := su.SanitizeErrorMessage(tt.input)

			// 检查应该包含的字符串
			for _, contain := range tt.contains {
				if !strings.Contains(result, contain) {
					t.Errorf("SanitizeErrorMessage() result %q should contain %q", result, contain)
				}
			}

			// 检查不应该包含的字符串
			for _, notContain := range tt.notContains {
				if strings.Contains(result, notContain) {
					t.Errorf("SanitizeErrorMessage() result %q should not contain %q", result, notContain)
				}
			}
		})
	}
}

func TestSecurityUtils_MaskEmail(t *testing.T) {
	su := NewSecurityUtils()

	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{
			name:     "Normal email",
			input:    "john.doe@example.com",
			expected: "j******e@example.com",
		},
		{
			name:     "Short username",
			input:    "ab@example.com",
			expected: "***@example.com",
		},
		{
			name:     "Single character username",
			input:    "a@example.com",
			expected: "***@example.com",
		},
		{
			name:     "Invalid email",
			input:    "not-an-email",
			expected: "not-an-email",
		},
		{
			name:     "Empty string",
			input:    "",
			expected: "",
		},
		{
			name:     "Email with subdomain",
			input:    "user.name@mail.company.com",
			expected: "u*******e@mail.company.com",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := su.MaskEmail(tt.input)
			if result != tt.expected {
				t.Errorf("MaskEmail() = %q, expected %q", result, tt.expected)
			}
		})
	}
}

func TestSecurityUtils_MaskIP(t *testing.T) {
	su := NewSecurityUtils()

	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{
			name:     "IPv4 address",
			input:    "192.168.1.100",
			expected: "*.*.*.100",
		},
		{
			name:     "Localhost IPv4",
			input:    "127.0.0.1",
			expected: "*.*.*.1",
		},
		{
			name:     "IPv6 address",
			input:    "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
			expected: "*:*:7334",
		},
		{
			name:     "Short IPv6",
			input:    "::1",
			expected: "*:*:1", // 实际结果
		},
		{
			name:     "Invalid IP",
			input:    "not-an-ip",
			expected: "not-an-ip",
		},
		{
			name:     "Empty string",
			input:    "",
			expected: "",
		},
		{
			name:     "IPv4 with port",
			input:    "192.168.1.100:8080",
			expected: "*.*.*.100:8080", // 应该只屏蔽IP部分
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := su.MaskIP(tt.input)
			if result != tt.expected {
				t.Errorf("MaskIP() = %q, expected %q", result, tt.expected)
			}
		})
	}
}

func TestSecurityUtils_HashSensitiveData(t *testing.T) {
	su := NewSecurityUtils()

	tests := []struct {
		name           string
		input          string
		expectedLength int
	}{
		{
			name:           "Normal string",
			input:          "sensitive-data-123",
			expectedLength: 64, // SHA256完整的64个字符的十六进制表示
		},
		{
			name:           "Empty string",
			input:          "",
			expectedLength: 0,
		},
		{
			name:           "Short string",
			input:          "abc",
			expectedLength: 64,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := su.HashSensitiveData(tt.input)
			if tt.input == "" {
				if result != "" {
					t.Errorf("HashSensitiveData() with empty input should return empty string, got %q", result)
				}
			} else {
				if len(result) != tt.expectedLength {
					t.Errorf("HashSensitiveData() returned %q with length %d, expected length %d", result, len(result), tt.expectedLength)
				}
				// 检查结果是否为有效的十六进制字符串
				for _, char := range result {
					if !((char >= '0' && char <= '9') || (char >= 'a' && char <= 'f')) {
						t.Errorf("HashSensitiveData() returned non-hex character: %c", char)
					}
				}
			}
		})
	}
}

func TestSecurityUtils_IsSensitivePath(t *testing.T) {
	su := NewSecurityUtils()

	// 测试基本功能
	if !su.IsSensitivePath("/home/user/project") {
		t.Error("Should detect home directory as sensitive")
	}

	if !su.IsSensitivePath("/root/secret") {
		t.Error("Should detect root directory as sensitive")
	}

	if su.IsSensitivePath("./config/app.yaml") {
		t.Error("Should not detect relative path as sensitive")
	}

	if su.IsSensitivePath("app.yaml") {
		t.Error("Should not detect simple filename as sensitive")
	}
}

func TestSecurityUtils_ExtractFilename(t *testing.T) {
	su := NewSecurityUtils()

	tests := []struct {
		name     string
		input    string
		expected string
	}{
		{
			name:     "Normal file path",
			input:    "/home/user/project/config.yaml",
			expected: "config.yaml",
		},
		{
			name:     "Simple filename",
			input:    "app.py",
			expected: "app.py",
		},
		{
			name:     "Path with spaces",
			input:    "/var/log/my app/error log.txt",
			expected: "error log.txt",
		},
		{
			name:     "Windows path",
			input:    "C:\\Users\\User\\Documents\\file.doc",
			expected: "C:\\Users\\User\\Documents\\file.doc", // Windows路径在Linux上filepath.Base的行为不同
		},
		{
			name:     "Empty string",
			input:    "",
			expected: "",
		},
		{
			name:     "Path ending with slash",
			input:    "/var/log/",
			expected: "log",
		},
		{
			name:     "Current directory reference",
			input:    "./config/../app.yaml",
			expected: "app.yaml",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := su.ExtractFilename(tt.input)
			if result != tt.expected {
				t.Errorf("ExtractFilename() = %q, expected %q", result, tt.expected)
			}
		})
	}
}

func TestSecurityUtils_SanitizeStackTrace(t *testing.T) {
	su := NewSecurityUtils()

	// 模拟堆栈跟踪
	stackTrace := `goroutine 1 [running]:
main.main()
	/home/user/project/main.go:123 +0x123
config.LoadConfig()
	/root/secret-project/internal/config/config.go:45 +0x789
utils.ProcessData()
	/opt/myapp/pkg/utils/data.go:89 +0xabc`

	result := su.SanitizeStackTrace(stackTrace)

	// 检查敏感路径已被清理
	sensitiveStrings := []string{"/home/user/", "/root/secret-project/", "/opt/myapp/"}
	for _, sensitive := range sensitiveStrings {
		if strings.Contains(result, sensitive) {
			t.Errorf("SanitizeStackTrace() should not contain sensitive path: %q", sensitive)
		}
	}

	// 检查文件名和行号仍然存在
	expectedStrings := []string{"main.go:123", "config.go:45", "data.go:89"}
	for _, expected := range expectedStrings {
		if !strings.Contains(result, expected) {
			t.Errorf("SanitizeStackTrace() should contain: %q", expected)
		}
	}

	// 检查函数名保持不变
	functionNames := []string{"main.main()", "config.LoadConfig()", "utils.ProcessData()"}
	for _, function := range functionNames {
		if !strings.Contains(result, function) {
			t.Errorf("SanitizeStackTrace() should contain function name: %q", function)
		}
	}
}

// 测试全局函数
func TestGlobalSecurityFunctions(t *testing.T) {
	// 测试全局SanitizePath函数
	path := "/home/user/secret/config.yaml"
	result := SanitizePath(path)
	if strings.Contains(result, "/home/user/") {
		t.Errorf("Global SanitizePath() should not contain sensitive path")
	}

	// 测试全局SanitizeErrorMessage函数
	message := "Error: password=secret123 in /etc/config/file"
	result = SanitizeErrorMessage(message)
	if strings.Contains(result, "secret123") || strings.Contains(result, "/etc/config/") {
		t.Errorf("Global SanitizeErrorMessage() should sanitize sensitive information")
	}

	// 测试全局MaskEmail函数
	email := "test.user@company.com"
	masked := MaskEmail(email)
	if masked == email {
		t.Errorf("Global MaskEmail() should mask the email")
	}

	// 测试全局MaskIP函数
	ip := "192.168.1.100"
	maskedIP := MaskIP(ip)
	if maskedIP == ip {
		t.Errorf("Global MaskIP() should mask the IP address")
	}
}
